iT邦幫忙

2024 iThome 鐵人賽

DAY 29
0

大家好,我是Karin。
因為鐵人賽挑戰也快告一段落了,所以想藉由實作一個簡單的東西來當作是這系列的一個小成果。
好像是算完成了一個雛型了,但還是有很多可以優化跟加強的地方。


第三天:檢查遊戲結果與遊戲完成提示
目標:實現完成條件判斷,新增遊戲完成提示。

正文開始:

檢查結果

思路

在圖片載入並切割圖片後,依序給予每個拼圖塊一個專屬的data-id,用來代表該拼圖塊的正確位置。
之後打亂拼圖塊,用data-current-id記錄該拼圖塊現在的位置,data-current-id會隨著使用者交換拼圖塊的動作而有所改變。
在每次使用者動作後(拼圖塊交換位置後),檢查每一個拼圖塊的 data-id 和 data-current-id 有無相同,若全部相同表示所有拼圖塊皆回到原本的位置上,顯示拼圖完成。

  • data-id 設置:
    data-id 現在基於 positions 陣列的原始順序來設置。表示每個拼圖塊的 data-id 為圖片切割後的正確位置,不會受到隨機打亂與後續使用者操作影響。
  • data-current-id 設置:
    data-current-id 基於打亂後的 shuffledPositions 設置,表示拼圖塊在隨機打亂後的當前位置,隨著拖放動作而變化。
  • 拼圖完成檢查:
    每次拼圖塊交換位置後,程式會檢查所有拼圖塊的 data-id 和 data-current-id 是否一致。如果拼圖塊的正確位置 (data-id) 與當前位置 (data-current-id) 相同,則拼圖完成。

計時功能

原理

  1. 開始計時
    記錄遊戲開始的時間(毫秒)。
startTime = new Date().getTime();

取得當前的時間,返回的值是自 1970 年 1 月 1 日以來的毫秒數。
(使用Unix Epoch作為時間的參考點,這是計算機中默認的一個標準時間基點)

2.計算完成時間
取得遊戲結束時的時間(毫秒),計算耗時,並將毫秒轉換為秒。

const endTime = new Date().getTime();
const timeUsed = Math.floor((endTime - startTime) / 1000);

3.顯示耗時

setTimeout(() => {
  alert(`恭喜你,拼圖完成了!\n用時:${timeUsed} 秒`);
}, 100);

這裡使用了 setTimeout(),將提示框的顯示稍微延遲 100 毫秒:
setTimeout():讓某件事情在一段時間後發生。

setTimeout(要執行的動作, 延遲的時間);

它像是一個鬧鐘,你告訴它過多久要做一件事,然後當時間到了,它就會自動執行你想做的事情。
時間單位一樣是毫秒。

因為在執行一些動作(如檢查拼圖完成)後,網頁需要一點時間來完成更新。使用 setTimeout 來確保在網頁更新完之後才彈出提示框,避免顯示上的衝突。


程式碼

document.addEventListener('DOMContentLoaded', function() {
  const imageUpload = document.getElementById('imageUpload');
  const puzzleContainer = document.getElementById('puzzle-container');
  let draggedTile = null; // 用來儲存當前被拖動的拼圖塊
  let startTime;

  // 監聽圖片上傳事件
  imageUpload.addEventListener('change', function(event) {
    const file = event.target.files[0]; // 取得上傳的圖片檔案
    if (file) {
      const reader = new FileReader();

      reader.onload = function(e) {
        const img = new Image();
        img.src = e.target.result;

        img.onload = function() {
          const rows = 3; // 固定為3行
          const cols = 3; // 固定為3列
          const containerWidth = puzzleContainer.clientWidth; // 取得容器的寬度
          const containerHeight = puzzleContainer.clientHeight; // 取得容器的高度

          // 根據容器大小計算每塊拼圖的寬高
          const tileWidth = containerWidth / cols;
          const tileHeight = containerHeight / rows;

          // 清除之前的拼圖
          puzzleContainer.innerHTML = '';

          // 設置拼圖容器的行列數
          puzzleContainer.style.gridTemplateColumns = `repeat(${cols}, 1fr)`;
          puzzleContainer.style.gridTemplateRows = `repeat(${rows}, 1fr)`;

          // 儲存圖片位置(背景位置)和正確的 ID
          const positions = [];

          for (let i = 0; i < rows * cols; i++) {
            positions.push({
              id: i, // 正確的位置 ID(原始順序)
              backgroundPosition: `${-(i % cols) * tileWidth}px ${-Math.floor(i / cols) * tileHeight}px`
            });
          }

          // 隨機打亂位置
          const shuffledPositions = [...positions].sort(() => Math.random() - 0.5);

          // 根據打亂後的順序生成拼圖塊
          for (let i = 0; i < rows * cols; i++) {
            const tile = document.createElement('div');
            tile.classList.add('puzzle-tile');
            tile.style.width = `${tileWidth}px`;
            tile.style.height = `${tileHeight}px`;
            tile.style.backgroundImage = `url(${img.src})`;
            tile.style.backgroundSize = `${containerWidth}px ${containerHeight}px`;
            tile.style.backgroundPosition = shuffledPositions[i].backgroundPosition;

            tile.draggable = true; // 設置拼圖塊為可拖動

            // 設置拼圖塊的正確位置 ID(保持原始圖片的順序)
            tile.setAttribute('data-id', positions[i].id); // 正確的位置 ID

            // 設置拼圖塊的當前位置 ID,初始化為打亂後的位置
            tile.setAttribute('data-current-id', shuffledPositions[i].id); // 當前的位置 ID

            // 拖動事件
            tile.addEventListener('dragstart', function() {
              draggedTile = tile; // 記錄當前被拖動的拼圖塊
            });

            // 設置拖放事件
            tile.addEventListener('dragover', function(e) {
              e.preventDefault(); // 允許放置
            });

            tile.addEventListener('drop', function() {
              if (draggedTile !== tile) {
                // 交換背景位置
                const draggedBackgroundPosition = draggedTile.style.backgroundPosition;
                draggedTile.style.backgroundPosition = tile.style.backgroundPosition;
                tile.style.backgroundPosition = draggedBackgroundPosition;

                // 交換 data-current-id
                const draggedCurrentId = draggedTile.getAttribute('data-current-id');
                const targetCurrentId = tile.getAttribute('data-current-id');

                draggedTile.setAttribute('data-current-id', targetCurrentId);
                tile.setAttribute('data-current-id', draggedCurrentId);

                // 打印交換後的 ID,用於檢查
                console.log(`拖動塊 ID: ${draggedTile.getAttribute('data-id')} => 當前 ID: ${draggedTile.getAttribute('data-current-id')}`);
                console.log(`目標塊 ID: ${tile.getAttribute('data-id')} => 當前 ID: ${tile.getAttribute('data-current-id')}`);
              }

              // 檢查拼圖是否完成
              checkPuzzleCompletion();
            });

            // 添加拼圖塊到容器
            puzzleContainer.appendChild(tile);
          }

          // 開始計時
          startTime = new Date().getTime();
        };
      };

      reader.readAsDataURL(file);
    }
  });

  // 檢查拼圖是否完成的函數
  function checkPuzzleCompletion() {
    const tiles = document.querySelectorAll('.puzzle-tile');
    let isComplete = true;

    tiles.forEach(tile => {
      const correctId = tile.getAttribute('data-id'); // 正確的位置 ID
      const currentId = tile.getAttribute('data-current-id'); // 當前的位置 ID
      console.log(`檢查: Tile ID: ${correctId}, 當前 ID: ${currentId}`); // 打印檢查信息
      if (correctId !== currentId) {
        isComplete = false;
      }
    });

    if (isComplete) {
      const endTime = new Date().getTime();
      const timeUsed = Math.floor((endTime - startTime) / 1000); // 以秒為單位

      setTimeout(() => {
        alert(`恭喜你,拼圖完成了!\n用時:${timeUsed} 秒`);
      }, 100);
    } else {
      console.log("拼圖未完成"); //測試用
    }
  }
});

結果

https://ithelp.ithome.com.tw/upload/images/20240928/201689671KXyW5Oaod.png
成功完成後跳出
https://ithelp.ithome.com.tw/upload/images/20240928/20168967wgaRs1Z9kW.png


上一篇
Day 28 實作練習-拼圖小遊戲-2
下一篇
DAY 30 總結
系列文
每天都進步一點!從零開始的JavaScript 與基礎網路知識學習30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言